/*
**  Copyright (c) 2014 Michael Liu(michael.liu.point@gmail.com).
**
**  Project: Gateway Unified Management Platform
**  File:    mtk7620_dev.c
**  Author:  Michael
**  Date:    03/20/2014
**
**  Purpose:
**    mtk7620 devices management implement.
*/

/* Include files. */

#include "common.h"
#include "dpmgr.h"
#include "eamgr.h"
#include "samgr.h"
#include "osdep.h"
#include "pfdep.h"
#include "mtk7620_local.h"

#include "sys/ioctl.h"
#include "sys/socket.h"
#include "linux/if.h"
#include "linux/autoconf.h"

#include "ra_ioctl.h"

/* Macro constant definitions. */

#define RT2860V2_AP_MOD	"/lib/modules/2.6.36/kernel/drivers/net/wireless/rt2860v2_ap/rt2860v2_ap.ko"
#define RLT_WIFI_MOD	"/lib/modules/2.6.36/kernel/drivers/net/wireless/rlt_wifi_ap/rlt_wifi.ko"

/* Type definitions. */

typedef struct {
	unsigned char power_save;	/* eBOOL_TRUE or eBOOL_FALSE */
	tgpio_opt_cfg led_24g[eSIGNAL_LED_MAX];
	tgpio_opt_cfg led_5g[eSIGNAL_LED_MAX];
	tgpio_opt_cfg power;
} trt_led_priv;

typedef struct {
	tgpio_opt_cfg button[eBUTTON_MAX];
} trt_button_priv;

typedef struct {
	tgpio_opt_cfg mode_sw;
} trt_mode_sw_priv;

/* Local function declarations. */

/* Macro API definitions. */

#define PREPARE_LED_ON_CFG(led_p) \
do {																		\
	(led_p)->on = 4000;														\
	(led_p)->off = 0;														\
	(led_p)->blinks = 1;													\
	(led_p)->rests = 0;														\
	(led_p)->times = 4000;													\
} while (0)

#define PREPARE_LED_OFF_CFG(led_p) \
do {																		\
	(led_p)->on = 0;														\
	(led_p)->off = 4000;													\
	(led_p)->blinks = 1;													\
	(led_p)->rests = 0; 													\
	(led_p)->times = 4000;													\
	(led_p)->on_delay = 0;													\
} while (0)

#define PREPARE_SIGNAL_LED_BLINK(led_array, time) \
do {																		\
	((led_array)[eSIGNAL_LED_LOW]).led.on = 5;								\
	((led_array)[eSIGNAL_LED_LOW]).led.off = 5;								\
	((led_array)[eSIGNAL_LED_LOW]).led.blinks = 1;							\
	((led_array)[eSIGNAL_LED_LOW]).led.rests = 1;							\
	((led_array)[eSIGNAL_LED_LOW]).led.times = time;						\
	((led_array)[eSIGNAL_LED_LOW]).led.on_delay = 0;						\
																			\
	((led_array)[eSIGNAL_LED_NORMAL]).led.on = 5; 							\
	((led_array)[eSIGNAL_LED_NORMAL]).led.off = 5;							\
	((led_array)[eSIGNAL_LED_NORMAL]).led.blinks = 1;						\
	((led_array)[eSIGNAL_LED_NORMAL]).led.rests = 1;						\
	((led_array)[eSIGNAL_LED_NORMAL]).led.times = time; 					\
	((led_array)[eSIGNAL_LED_NORMAL]).led.on_delay = 0;						\
																			\
	((led_array)[eSIGNAL_LED_HIGH]).led.on = 5; 							\
	((led_array)[eSIGNAL_LED_HIGH]).led.off = 5;							\
	((led_array)[eSIGNAL_LED_HIGH]).led.blinks = 1;							\
	((led_array)[eSIGNAL_LED_HIGH]).led.rests = 1;							\
	((led_array)[eSIGNAL_LED_HIGH]).led.times = time; 						\
	((led_array)[eSIGNAL_LED_HIGH]).led.on_delay = 0;						\
} while (0)

#define PREPARE_SIGNAL_LED_BLINK_FAST(led_array, time) \
do {																		\
	((led_array)[eSIGNAL_LED_LOW]).led.on = 2;								\
	((led_array)[eSIGNAL_LED_LOW]).led.off = 2;								\
	((led_array)[eSIGNAL_LED_LOW]).led.blinks = 1;							\
	((led_array)[eSIGNAL_LED_LOW]).led.rests = 1;							\
	((led_array)[eSIGNAL_LED_LOW]).led.times = time;						\
	((led_array)[eSIGNAL_LED_LOW]).led.on_delay = 0;						\
																			\
	((led_array)[eSIGNAL_LED_NORMAL]).led.on = 2; 							\
	((led_array)[eSIGNAL_LED_NORMAL]).led.off = 2;							\
	((led_array)[eSIGNAL_LED_NORMAL]).led.blinks = 1;						\
	((led_array)[eSIGNAL_LED_NORMAL]).led.rests = 1;						\
	((led_array)[eSIGNAL_LED_NORMAL]).led.times = time; 					\
	((led_array)[eSIGNAL_LED_NORMAL]).led.on_delay = 0;						\
																			\
	((led_array)[eSIGNAL_LED_HIGH]).led.on = 2; 							\
	((led_array)[eSIGNAL_LED_HIGH]).led.off = 2;							\
	((led_array)[eSIGNAL_LED_HIGH]).led.blinks = 1;							\
	((led_array)[eSIGNAL_LED_HIGH]).led.rests = 1;							\
	((led_array)[eSIGNAL_LED_HIGH]).led.times = time; 						\
	((led_array)[eSIGNAL_LED_HIGH]).led.on_delay = 0;						\
} while (0)

#define PREPARE_SIGNAL_LED_MARQUEE(led_array, time) \
do {																		\
	((led_array)[eSIGNAL_LED_LOW]).led.on = 5;								\
	((led_array)[eSIGNAL_LED_LOW]).led.off = 15;							\
	((led_array)[eSIGNAL_LED_LOW]).led.blinks = ((time == 1) ? 2 : time);	\
	((led_array)[eSIGNAL_LED_LOW]).led.rests = 1;							\
	((led_array)[eSIGNAL_LED_LOW]).led.times = time;						\
	((led_array)[eSIGNAL_LED_LOW]).led.on_delay = 20;						\
																			\
	((led_array)[eSIGNAL_LED_NORMAL]).led.on = 5; 							\
	((led_array)[eSIGNAL_LED_NORMAL]).led.off = 15;							\
	((led_array)[eSIGNAL_LED_NORMAL]).led.blinks = ((time == 1) ? 2 : time);\
	((led_array)[eSIGNAL_LED_NORMAL]).led.rests = 1;						\
	((led_array)[eSIGNAL_LED_NORMAL]).led.times = time; 					\
	((led_array)[eSIGNAL_LED_NORMAL]).led.on_delay = 15;					\
																			\
	((led_array)[eSIGNAL_LED_HIGH]).led.on = 5; 							\
	((led_array)[eSIGNAL_LED_HIGH]).led.off = 15;							\
	((led_array)[eSIGNAL_LED_HIGH]).led.blinks = ((time == 1) ? 2 : time);	\
	((led_array)[eSIGNAL_LED_HIGH]).led.rests = 1;							\
	((led_array)[eSIGNAL_LED_HIGH]).led.times = time; 						\
	((led_array)[eSIGNAL_LED_HIGH]).led.on_delay = 10;						\
} while (0)

#define PREPARE_SIGNAL_LED_MARQUEE_REVERSE(led_array, time) \
do {																		\
	((led_array)[eSIGNAL_LED_LOW]).led.on = 5;								\
	((led_array)[eSIGNAL_LED_LOW]).led.off = 15;							\
	((led_array)[eSIGNAL_LED_LOW]).led.blinks = ((time == 1) ? 2 : time);	\
	((led_array)[eSIGNAL_LED_LOW]).led.rests = 1;							\
	((led_array)[eSIGNAL_LED_LOW]).led.times = time;						\
	((led_array)[eSIGNAL_LED_LOW]).led.on_delay = 10;						\
																			\
	((led_array)[eSIGNAL_LED_NORMAL]).led.on = 5; 							\
	((led_array)[eSIGNAL_LED_NORMAL]).led.off = 15;							\
	((led_array)[eSIGNAL_LED_NORMAL]).led.blinks = ((time == 1) ? 2 : time);\
	((led_array)[eSIGNAL_LED_NORMAL]).led.rests = 1;						\
	((led_array)[eSIGNAL_LED_NORMAL]).led.times = time; 					\
	((led_array)[eSIGNAL_LED_NORMAL]).led.on_delay = 15;					\
																			\
	((led_array)[eSIGNAL_LED_HIGH]).led.on = 5; 							\
	((led_array)[eSIGNAL_LED_HIGH]).led.off = 15;							\
	((led_array)[eSIGNAL_LED_HIGH]).led.blinks = ((time == 1) ? 2 : time);	\
	((led_array)[eSIGNAL_LED_HIGH]).led.rests = 1;							\
	((led_array)[eSIGNAL_LED_HIGH]).led.times = time; 						\
	((led_array)[eSIGNAL_LED_HIGH]).led.on_delay = 20;						\
} while (0)

/* Global variable declarations. */

tdp_dev raeth = {
	/* .name = */ "raeth",
	/* .prepare = */ eBOOL_FALSE,
	/* .based_on_dev */ NULL,
};

tdp_dev rt2860v2_ap = {
	/* .name = */ "rt2860v2_ap",
	/* .prepare = */ eBOOL_FALSE,
	/* .based_on_dev */ NULL,
};

tdp_dev rlt_wifi = {
	/* .name = */ "rlt_wifi",
	/* .prepare = */ eBOOL_FALSE,
	/* .based_on_dev */ NULL,
};

tsa_dev rt_gpio = {
	/* .name = */ "rt_gpio",
	/* .prepare = */ eBOOL_FALSE,
	/* .init = */ eBOOL_FALSE,
	/* .based_on_dev */ NULL,
};

tsa_dev rt_led = {
	/* .name = */ "rt_led",
	/* .prepare = */ eBOOL_FALSE,
	/* .init = */ eBOOL_FALSE,
	/* .based_on_dev */ &rt_gpio,
};

tsa_dev rt_button = {
	/* .name = */ "rt_button",
	/* .prepare = */ eBOOL_FALSE,
	/* .init = */ eBOOL_FALSE,
	/* .based_on_dev */ &rt_gpio,
};

tsa_dev rt_mode_sw = {
	/* .name = */ "rt_mode_sw",
	/* .prepare = */ eBOOL_FALSE,
	/* .init = */ eBOOL_FALSE,
	/* .based_on_dev */ &rt_gpio,
};

e_ret init_raeth_dev(tdp_dev * dp_dev)
{

	int esw_fd;
	unsigned int portmap;

	/* init ralink embedded switch to setup eth2 data path. */

	esw_fd = raesw_init();
	if (esw_fd < 0) {
		return (eRET_FAILURE);
	}

	/*
	   #LAN/WAN ports as security mode
	   switch reg w 2004 ff0003 #port0
	   switch reg w 2104 ff0003 #port1
	   switch reg w 2204 ff0003 #port2
	   switch reg w 2304 ff0003 #port3
	   switch reg w 2404 ff0003 #port4
	   switch reg w 2504 ff0003 #port5
	 */

	raesw_reg_write(esw_fd, 0x2004, 0xff0003);
	raesw_reg_write(esw_fd, 0x2104, 0xff0003);
	raesw_reg_write(esw_fd, 0x2204, 0xff0003);
	raesw_reg_write(esw_fd, 0x2304, 0xff0003);
	raesw_reg_write(esw_fd, 0x2404, 0xff0003);
	raesw_reg_write(esw_fd, 0x2504, 0xff0003);

	/*
	   #LAN/WAN ports as transparent port
	   switch reg w 2010 810000c0 #port0
	   switch reg w 2110 810000c0 #port1
	   switch reg w 2210 810000c0 #port2
	   switch reg w 2310 810000c0 #port3
	   switch reg w 2410 810000c0 #port4
	   switch reg w 2510 810000c0 #port5
	 */

	raesw_reg_write(esw_fd, 0x2010, 0x810000c0);
	raesw_reg_write(esw_fd, 0x2110, 0x810000c0);
	raesw_reg_write(esw_fd, 0x2210, 0x810000c0);
	raesw_reg_write(esw_fd, 0x2310, 0x810000c0);
	raesw_reg_write(esw_fd, 0x2410, 0x810000c0);
	raesw_reg_write(esw_fd, 0x2510, 0x810000c0);

	/*
	   #set CPU/P7 port as user port
	   switch reg w 2610 81000000 #port6
	   switch reg w 2710 81000000 #port7
	 */

	raesw_reg_write(esw_fd, 0x2610, 0x81000000);
	raesw_reg_write(esw_fd, 0x2710, 0x81000000);

	/*
	   switch reg w 2604 20ff0003 #port6, Egress VLAN Tag Attribution=tagged
	   switch reg w 2704 20ff0003 #port7, Egress VLAN Tag Attribution=tagged
	 */

	raesw_reg_write(esw_fd, 0x2604, 0x20ff0003);
	raesw_reg_write(esw_fd, 0x2704, 0x20ff0003);

	/*
	   #echo "Special Tag Disabled"
	   switch reg w 2610 81000000 #port6
	 */

	raesw_reg_write(esw_fd, 0x2610, 0x81000000);

	/*
	   #set PVID
	   switch reg w 2014 10001 #port0
	   switch reg w 2114 10001 #port1
	   switch reg w 2214 10001 #port2
	   switch reg w 2314 10001 #port3
	   switch reg w 2414 10002 #port4
	   switch reg w 2514 10001 #port5
	 */

	raesw_reg_write(esw_fd, 0x2014, 0x10001);
	raesw_reg_write(esw_fd, 0x2114, 0x10001);
	raesw_reg_write(esw_fd, 0x2214, 0x10001);
	raesw_reg_write(esw_fd, 0x2314, 0x10001);
	raesw_reg_write(esw_fd, 0x2414, 0x10002);
	raesw_reg_write(esw_fd, 0x2514, 0x10001);

	/*
	   #VLAN member port
	   switch vlan set 0 1 11110111
	   switch vlan set 1 2 00001011
	 */
	portmap = 0;
	mask_set_bit(portmap, 0);
	mask_set_bit(portmap, 1);
	mask_set_bit(portmap, 2);
	mask_set_bit(portmap, 3);
	mask_set_bit(portmap, 5);
	mask_set_bit(portmap, 6);
	mask_set_bit(portmap, 7);
	raesw_vlan_set(esw_fd, 0, 1, portmap, 0, eBOOL_FALSE, eBOOL_FALSE);

	portmap = 0;
	mask_set_bit(portmap, 4);
	mask_set_bit(portmap, 6);
	mask_set_bit(portmap, 7);
	raesw_vlan_set(esw_fd, 1, 2, portmap, 0, eBOOL_FALSE, eBOOL_FALSE);

	/*
	   switch clear
	 */
	raesw_table_clear(esw_fd);

	raesw_finish(esw_fd);
	return (eRET_SUCCESS);
}

e_ret clean_raeth_dev(tdp_dev * dp_dev)
{
	return (eRET_SUCCESS);
}

e_ret init_rt2860v2_ap_dev(tdp_dev * dp_dev)
{

	/* prepare rt2860v2_ap DAT config file. */
	/* TODO: replace this with default DAT file. */
	os_dep_do_system("ralink_init", "make_wireless_config", "rt2860", NULL);

	/* Insmod: load device driver. */
	os_dep_do_system("insmod", "-q", RT2860V2_AP_MOD, NULL);
	return (eRET_SUCCESS);
}

e_ret clean_rt2860v2_ap_dev(tdp_dev * dp_dev)
{

	/* rmmod: unload device driver. */
	os_dep_do_system("rmmod", RT2860V2_AP_MOD, NULL);
	return (eRET_SUCCESS);
}

e_ret init_rlt_wifi_dev(tdp_dev * dp_dev)
{

	//INIT_LIST_HEAD(&rlt_wifi.based_on_dev);
	/* prepare rlt wifi DAT config file. */
	/* TODO: replace this with default DAT file. */
	os_dep_do_system("ralink_init", "make_wireless_config", "rtdev", NULL);

	/* Insmod: load device driver. */
	os_dep_do_system("insmod", "-q", RLT_WIFI_MOD, NULL);
	return (eRET_SUCCESS);
}

e_ret clean_rlt_wifi_dev(tdp_dev * dp_dev)
{

	/* rmmod: unload device driver. */
	os_dep_do_system("rmmod", RLT_WIFI_MOD, NULL);
	return (eRET_SUCCESS);
}

e_ret init_rt_gpio_dev(tsa_dev * sa_dev)
{

	int dev_fd;

	if (sa_dev->init == eBOOL_TRUE) {
		sysmgr_dbg("sa dev[%s] already init.\n", sa_dev->name);
		return (eRET_SUCCESS);
	}

	sa_dev->dev_fd = -1;

	dev_fd = open(MTK_GPIO_DEVICE, O_RDONLY);
	if (dev_fd < 0) {
		sysmgr_err("open(%s) failed, err: %d,[%s]\n", MTK_GPIO_DEVICE, errno, strerror(errno));
		return (eRET_FAILURE);
	}

	sa_dev->dev_fd = dev_fd;
	sa_dev->init = eBOOL_TRUE;

	return (eRET_SUCCESS);
}

e_ret clean_rt_gpio_dev(tsa_dev * sa_dev)
{
	if (sa_dev->init == eBOOL_FALSE) {
		sysmgr_dbg("sa dev[%s] already clean.\n", sa_dev->name);
		return (eRET_SUCCESS);
	}

	sa_dev->init = eBOOL_FALSE;

	if (sa_dev->dev_fd >= 0) {
		close(sa_dev->dev_fd);
		sa_dev->dev_fd = -1;
	}
	return (eRET_SUCCESS);
}

e_ret rt_gpio_dev_opt(tsa_dev * sa_dev, unsigned char opt, void *cfg)
{
	e_ret ret;
	int dev_fd = sa_dev->dev_fd;
	tgpio_opt_cfg *gpio_cfg = cfg;

	ret = eRET_SUCCESS;
	switch (opt) {
	case eGPIO_OPT_INIT:
		ret = init_rt_gpio_dev(sa_dev);
		break;

	case eGPIO_OPT_SET_DIR:
		ret = mtk_gpio_set_dir(dev_fd, gpio_cfg->gpio, gpio_cfg->dir);
		break;

	case eGPIO_OPT_READ:
		ret = mtk_gpio_read(dev_fd, gpio_cfg->gpio, &gpio_cfg->rw_value);
		break;

	case eGPIO_OPT_WRITE:
		ret = mtk_gpio_write(dev_fd, gpio_cfg->gpio, gpio_cfg->rw_value);
		break;

	case eGPIO_OPT_ENABLE_IRQ:
		ret = mtk_gpio_set_irq(dev_fd, eBOOL_TRUE);
		break;

	case eGPIO_OPT_DISABLE_IRQ:
		ret = mtk_gpio_set_irq(dev_fd, eBOOL_FALSE);
		break;

	case eGPIO_OPT_REG_PROCCESS:
		ret = mtk_gpio_reg_proc(dev_fd, gpio_cfg->gpio, gpio_cfg->pid);
		break;

	case eGPIO_OPT_GET_INTP:
		ret = mtk_gpio_get_intp(dev_fd, gpio_cfg->gpio, &gpio_cfg->intp);
		break;

	case eGPIO_OPT_SET_LED:
		ret = mtk_gpio_set_led(dev_fd, gpio_cfg->gpio, &gpio_cfg->led);
		break;

	case eGPIO_OPT_CLEAN:
		ret = clean_rt_gpio_dev(sa_dev);
		break;
	}

	return (ret);
}

e_ret init_rt_led_dev(tsa_dev * sa_dev)
{

	tsa_dev *gpio;
	const char *val;
	trt_led_priv *priv;

	if (sa_dev->init == eBOOL_TRUE) {
		sysmgr_dbg("sa dev[%s] already init.\n", sa_dev->name);
		return (eRET_SUCCESS);
	}

	priv = malloc(sizeof(*priv));
	if (priv == NULL) {
		sysmgr_err("malloc(%d) failed, err: %d,[%s]\n", sizeof(*priv), errno, strerror(errno));
		return (eRET_NO_RESOURCE);
	}

	memset(priv, 0, sizeof(*priv));

	/* Set up LED parameters */
	val = pf_dep_cfg_get_val(RT2860_NVRAM, DEVICE_LED_POWER_SAVE, DEFAULT_LED_POWER_SAVE_ON);
	priv->power_save = (val[0] == LED_POWER_SAVE_ON) ? eBOOL_TRUE : eBOOL_FALSE;

	priv->led_24g[eSIGNAL_LED_LOW].gpio = eGPIO_NO_63;
	priv->led_24g[eSIGNAL_LED_NORMAL].gpio = eGPIO_NO_62;
	priv->led_24g[eSIGNAL_LED_HIGH].gpio = eGPIO_NO_61;
	priv->led_5g[eSIGNAL_LED_LOW].gpio = eGPIO_NO_60;
	priv->led_5g[eSIGNAL_LED_NORMAL].gpio = eGPIO_NO_64;
	priv->led_5g[eSIGNAL_LED_HIGH].gpio = eGPIO_NO_65;
	priv->power.gpio = eGPIO_NO_09;

	PREPARE_LED_ON_CFG(&(priv->power.led));

	PREPARE_SIGNAL_LED_MARQUEE(priv->led_24g, 4000);
	PREPARE_SIGNAL_LED_MARQUEE(priv->led_5g, 4000);

	/* Turn on all LEDs. */
	gpio = sa_dev->based_on_dev;
	if (gpio != NULL) {
		if (gpio->opt != NULL) {
			gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_24g[eSIGNAL_LED_LOW]);
			gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_5g[eSIGNAL_LED_LOW]);

			gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_24g[eSIGNAL_LED_NORMAL]);
			gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_5g[eSIGNAL_LED_NORMAL]);

			gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_24g[eSIGNAL_LED_HIGH]);
			gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_5g[eSIGNAL_LED_HIGH]);

			gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->power);
		} else {
			sysmgr_err("sa dev[%s] base on dev[%s] have NO opt method\n", sa_dev->name, gpio->name);
		}
	} else {
		sysmgr_err("sa dev[%s] lose base on dev\n", sa_dev->name);
	}

	sa_dev->init = eBOOL_TRUE;
	sa_dev->priv = priv;

//#define LED_OPT_TEST
#ifdef LED_OPT_TEST
	{

		tled_opt_cfg cfg;

		e_ret rt_led_dev_opt(tsa_dev * sa_dev, unsigned char opt, void *cfg);

		sleep(3);

		rt_led_dev_opt(sa_dev, eLED_OPT_SET_24G_SIGNAL_LOW, NULL);
		sleep(1);

		rt_led_dev_opt(sa_dev, eLED_OPT_SET_24G_SIGNAL_NORMAL, NULL);
		sleep(1);

		rt_led_dev_opt(sa_dev, eLED_OPT_SET_24G_SIGNAL_HIGH, NULL);
		sleep(1);

		rt_led_dev_opt(sa_dev, eLED_OPT_SET_24G_SIGNAL_NONE, NULL);
		sleep(1);

		rt_led_dev_opt(sa_dev, eLED_OPT_SET_24G_SIGNAL_LOW, NULL);
		sleep(1);

		rt_led_dev_opt(sa_dev, eLED_OPT_SET_24G_SIGNAL_NORMAL, NULL);
		sleep(1);

		rt_led_dev_opt(sa_dev, eLED_OPT_SET_24G_SIGNAL_HIGH, NULL);
		sleep(1);

		rt_led_dev_opt(sa_dev, eLED_OPT_SET_24G_SCAN, NULL);
		sleep(2);
		rt_led_dev_opt(sa_dev, eLED_OPT_SET_24G_WPS_PROCESS, NULL);
		sleep(1);

		rt_led_dev_opt(sa_dev, eLED_OPT_SET_24G_WPS_SUCCESS, NULL);
		sleep(2);

		rt_led_dev_opt(sa_dev, eLED_OPT_SET_24G_WPS_FAIL, NULL);
		sleep(4);

		rt_led_dev_opt(sa_dev, eLED_OPT_SET_5G_SIGNAL_NONE, NULL);
		sleep(1);

		rt_led_dev_opt(sa_dev, eLED_OPT_SET_5G_SIGNAL_LOW, NULL);
		sleep(1);

		rt_led_dev_opt(sa_dev, eLED_OPT_SET_5G_SIGNAL_NORMAL, NULL);
		sleep(1);

		rt_led_dev_opt(sa_dev, eLED_OPT_SET_5G_SIGNAL_HIGH, NULL);
		sleep(1);

		rt_led_dev_opt(sa_dev, eLED_OPT_SET_5G_SCAN, NULL);
		sleep(2);
		rt_led_dev_opt(sa_dev, eLED_OPT_SET_5G_WPS_PROCESS, NULL);
		sleep(1);

		rt_led_dev_opt(sa_dev, eLED_OPT_SET_5G_WPS_SUCCESS, NULL);
		sleep(2);

		rt_led_dev_opt(sa_dev, eLED_OPT_SET_5G_WPS_FAIL, NULL);
		sleep(4);

		cfg.power_save = eBOOL_TRUE;
		rt_led_dev_opt(sa_dev, eLED_OPT_SET_POWER_SAVE, &cfg);

		sleep(1);
		cfg.power_save = eBOOL_FALSE;
		rt_led_dev_opt(sa_dev, eLED_OPT_SET_POWER_SAVE, &cfg);

	}
#endif /* LED_OPT_TEST */

	return (eRET_SUCCESS);
}

e_ret clean_rt_led_dev(tsa_dev * sa_dev)
{
	if (sa_dev->init == eBOOL_FALSE) {
		sysmgr_dbg("sa dev[%s] already clean.\n", sa_dev->name);
		return (eRET_SUCCESS);
	}

	if (sa_dev->priv != NULL) {
		free(sa_dev->priv);
		sa_dev->priv = NULL;
	}

	sa_dev->init = eBOOL_FALSE;
	return (eRET_SUCCESS);
}

e_ret rt_led_dev_opt(tsa_dev * sa_dev, unsigned char opt, void *cfg)
{
	e_ret ret;
	tsa_dev *gpio = sa_dev->based_on_dev;
	trt_led_priv *priv = sa_dev->priv;
	tled_opt_cfg *led_cfg = cfg;

	if ((opt != eLED_OPT_INIT) && (priv == NULL)) {
		sysmgr_err("sa dev[%s] invalid status, priv == NULL\n", sa_dev->name, priv);
		return (eRET_INVALID_STATE);
	}

	if (gpio == NULL) {
		sysmgr_err("sa dev[%s] lose base on dev\n", sa_dev->name);
		return (eRET_INVALID_STATE);
	}

	ret = eRET_SUCCESS;
	switch (opt) {
	case eLED_OPT_INIT:
		ret = init_rt_led_dev(sa_dev);
		break;

	case eLED_OPT_GET_POWER_SAVE:
		led_cfg->power_save = priv->power_save;
		break;

	case eLED_OPT_SET_POWER_SAVE:
		{
			tgpio_opt_cfg led_24g[eSIGNAL_LED_MAX];
			tgpio_opt_cfg led_5g[eSIGNAL_LED_MAX];
			tgpio_opt_cfg power;

			led_24g[eSIGNAL_LED_HIGH] = priv->led_24g[eSIGNAL_LED_HIGH];
			led_24g[eSIGNAL_LED_NORMAL] = priv->led_24g[eSIGNAL_LED_NORMAL];
			led_24g[eSIGNAL_LED_LOW] = priv->led_24g[eSIGNAL_LED_LOW];
			led_5g[eSIGNAL_LED_HIGH] = priv->led_5g[eSIGNAL_LED_HIGH];
			led_5g[eSIGNAL_LED_NORMAL] = priv->led_5g[eSIGNAL_LED_NORMAL];
			led_5g[eSIGNAL_LED_LOW] = priv->led_5g[eSIGNAL_LED_LOW];
			power = priv->power;

			if (led_cfg->power_save == eBOOL_TRUE) {
				PREPARE_LED_OFF_CFG(&(led_24g[eSIGNAL_LED_HIGH].led));
				PREPARE_LED_OFF_CFG(&(led_24g[eSIGNAL_LED_NORMAL].led));
				PREPARE_LED_OFF_CFG(&(led_24g[eSIGNAL_LED_LOW].led));
				PREPARE_LED_OFF_CFG(&(led_5g[eSIGNAL_LED_HIGH].led));
				PREPARE_LED_OFF_CFG(&(led_5g[eSIGNAL_LED_NORMAL].led));
				PREPARE_LED_OFF_CFG(&(led_5g[eSIGNAL_LED_LOW].led));
				PREPARE_LED_OFF_CFG(&(power.led));
				pf_dep_cfg_set_val(RT2860_NVRAM, DEVICE_LED_POWER_SAVE, "Y");
			} else {
				pf_dep_cfg_set_val(RT2860_NVRAM, DEVICE_LED_POWER_SAVE, "N");
			}

			if (gpio->opt != NULL) {
				gpio->opt(gpio, eGPIO_OPT_SET_LED, &led_24g[eSIGNAL_LED_LOW]);
				gpio->opt(gpio, eGPIO_OPT_SET_LED, &led_24g[eSIGNAL_LED_NORMAL]);
				gpio->opt(gpio, eGPIO_OPT_SET_LED, &led_24g[eSIGNAL_LED_HIGH]);

				gpio->opt(gpio, eGPIO_OPT_SET_LED, &led_5g[eSIGNAL_LED_LOW]);
				gpio->opt(gpio, eGPIO_OPT_SET_LED, &led_5g[eSIGNAL_LED_NORMAL]);
				gpio->opt(gpio, eGPIO_OPT_SET_LED, &led_5g[eSIGNAL_LED_HIGH]);

				gpio->opt(gpio, eGPIO_OPT_SET_LED, &power);
			} else {
				sysmgr_err("sa dev[%s] base on dev[%s] have NO opt method\n", sa_dev->name, gpio->name);
			}

			priv->power_save = led_cfg->power_save;
		}
		break;

	case eLED_OPT_SET_24G_SIGNAL_HIGH:
		PREPARE_LED_ON_CFG(&(priv->led_24g[eSIGNAL_LED_HIGH].led));
		PREPARE_LED_ON_CFG(&(priv->led_24g[eSIGNAL_LED_NORMAL].led));
		PREPARE_LED_ON_CFG(&(priv->led_24g[eSIGNAL_LED_LOW].led));
		if (gpio->opt != NULL) {
			if (priv->power_save != eBOOL_TRUE) {
				gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_24g[eSIGNAL_LED_LOW]);
				gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_24g[eSIGNAL_LED_NORMAL]);
				gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_24g[eSIGNAL_LED_HIGH]);
			}
		} else {
			sysmgr_err("sa dev[%s] base on dev[%s] have NO opt method\n", sa_dev->name, gpio->name);
		}
		break;

	case eLED_OPT_SET_24G_SIGNAL_NORMAL:
		PREPARE_LED_OFF_CFG(&(priv->led_24g[eSIGNAL_LED_HIGH].led));
		PREPARE_LED_ON_CFG(&(priv->led_24g[eSIGNAL_LED_NORMAL].led));
		PREPARE_LED_ON_CFG(&(priv->led_24g[eSIGNAL_LED_LOW].led));
		if (gpio->opt != NULL) {
			if (priv->power_save != eBOOL_TRUE) {
				gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_24g[eSIGNAL_LED_LOW]);
				gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_24g[eSIGNAL_LED_NORMAL]);
				gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_24g[eSIGNAL_LED_HIGH]);
			}
		} else {
			sysmgr_err("sa dev[%s] base on dev[%s] have NO opt method\n", sa_dev->name, gpio->name);
		}
		break;

	case eLED_OPT_SET_24G_SIGNAL_LOW:
		PREPARE_LED_OFF_CFG(&(priv->led_24g[eSIGNAL_LED_HIGH].led));
		PREPARE_LED_OFF_CFG(&(priv->led_24g[eSIGNAL_LED_NORMAL].led));
		PREPARE_LED_ON_CFG(&(priv->led_24g[eSIGNAL_LED_LOW].led));
		if (gpio->opt != NULL) {
			if (priv->power_save != eBOOL_TRUE) {
				gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_24g[eSIGNAL_LED_LOW]);
				gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_24g[eSIGNAL_LED_NORMAL]);
				gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_24g[eSIGNAL_LED_HIGH]);
			}
		} else {
			sysmgr_err("sa dev[%s] base on dev[%s] have NO opt method\n", sa_dev->name, gpio->name);
		}
		break;

	case eLED_OPT_SET_24G_SIGNAL_NONE:
		PREPARE_LED_OFF_CFG(&(priv->led_24g[eSIGNAL_LED_HIGH].led));
		PREPARE_LED_OFF_CFG(&(priv->led_24g[eSIGNAL_LED_NORMAL].led));
		PREPARE_LED_OFF_CFG(&(priv->led_24g[eSIGNAL_LED_LOW].led));
		if (gpio->opt != NULL) {
			/* turn off led, ignore power save */
			gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_24g[eSIGNAL_LED_LOW]);
			gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_24g[eSIGNAL_LED_NORMAL]);
			gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_24g[eSIGNAL_LED_HIGH]);
		} else {
			sysmgr_err("sa dev[%s] base on dev[%s] have NO opt method\n", sa_dev->name, gpio->name);
		}
		break;

	case eLED_OPT_SET_24G_SCAN:
		PREPARE_SIGNAL_LED_MARQUEE(priv->led_24g, 4000);
		if (gpio->opt != NULL) {
			if (priv->power_save != eBOOL_TRUE) {
				gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_24g[eSIGNAL_LED_LOW]);
				gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_24g[eSIGNAL_LED_NORMAL]);
				gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_24g[eSIGNAL_LED_HIGH]);
			}
		} else {
			sysmgr_err("sa dev[%s] base on dev[%s] have NO opt method\n", sa_dev->name, gpio->name);
		}
		break;

	case eLED_OPT_SET_24G_WPS_PROCESS:
		PREPARE_SIGNAL_LED_MARQUEE_REVERSE(priv->led_24g, 4000);
		if (gpio->opt != NULL) {
			if (priv->power_save != eBOOL_TRUE) {
				gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_24g[eSIGNAL_LED_LOW]);
				gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_24g[eSIGNAL_LED_NORMAL]);
				gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_24g[eSIGNAL_LED_HIGH]);
			}
		} else {
			sysmgr_err("sa dev[%s] base on dev[%s] have NO opt method\n", sa_dev->name, gpio->name);
		}
		break;

	case eLED_OPT_SET_24G_WPS_SUCCESS:
		PREPARE_SIGNAL_LED_BLINK(priv->led_24g, 5);
		if (gpio->opt != NULL) {
			if (priv->power_save != eBOOL_TRUE) {
				gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_24g[eSIGNAL_LED_LOW]);
				gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_24g[eSIGNAL_LED_NORMAL]);
				gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_24g[eSIGNAL_LED_HIGH]);
			}
		} else {
			sysmgr_err("sa dev[%s] base on dev[%s] have NO opt method\n", sa_dev->name, gpio->name);
		}
		break;

	case eLED_OPT_SET_24G_WPS_FAIL:
	case eLED_OPT_SET_24G_WPS_OVERLAP:
		PREPARE_SIGNAL_LED_BLINK_FAST(priv->led_24g, 10);
		if (gpio->opt != NULL) {
			if (priv->power_save != eBOOL_TRUE) {
				gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_24g[eSIGNAL_LED_LOW]);
				gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_24g[eSIGNAL_LED_NORMAL]);
				gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_24g[eSIGNAL_LED_HIGH]);
			}
		} else {
			sysmgr_err("sa dev[%s] base on dev[%s] have NO opt method\n", sa_dev->name, gpio->name);
		}
		break;

	case eLED_OPT_SET_5G_SIGNAL_HIGH:
		PREPARE_LED_ON_CFG(&(priv->led_5g[eSIGNAL_LED_HIGH].led));
		PREPARE_LED_ON_CFG(&(priv->led_5g[eSIGNAL_LED_NORMAL].led));
		PREPARE_LED_ON_CFG(&(priv->led_5g[eSIGNAL_LED_LOW].led));
		if (gpio->opt != NULL) {
			if (priv->power_save != eBOOL_TRUE) {
				gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_5g[eSIGNAL_LED_LOW]);
				gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_5g[eSIGNAL_LED_NORMAL]);
				gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_5g[eSIGNAL_LED_HIGH]);
			}
		} else {
			sysmgr_err("sa dev[%s] base on dev[%s] have NO opt method\n", sa_dev->name, gpio->name);
		}
		break;

	case eLED_OPT_SET_5G_SIGNAL_NORMAL:
		PREPARE_LED_OFF_CFG(&(priv->led_5g[eSIGNAL_LED_HIGH].led));
		PREPARE_LED_ON_CFG(&(priv->led_5g[eSIGNAL_LED_NORMAL].led));
		PREPARE_LED_ON_CFG(&(priv->led_5g[eSIGNAL_LED_LOW].led));
		if (gpio->opt != NULL) {
			if (priv->power_save != eBOOL_TRUE) {
				gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_5g[eSIGNAL_LED_LOW]);
				gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_5g[eSIGNAL_LED_NORMAL]);
				gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_5g[eSIGNAL_LED_HIGH]);
			}
		} else {
			sysmgr_err("sa dev[%s] base on dev[%s] have NO opt method\n", sa_dev->name, gpio->name);
		}
		break;
	case eLED_OPT_SET_5G_SIGNAL_LOW:
		PREPARE_LED_OFF_CFG(&(priv->led_5g[eSIGNAL_LED_HIGH].led));
		PREPARE_LED_OFF_CFG(&(priv->led_5g[eSIGNAL_LED_NORMAL].led));
		PREPARE_LED_ON_CFG(&(priv->led_5g[eSIGNAL_LED_LOW].led));
		if (gpio->opt != NULL) {
			if (priv->power_save != eBOOL_TRUE) {
				gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_5g[eSIGNAL_LED_LOW]);
				gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_5g[eSIGNAL_LED_NORMAL]);
				gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_5g[eSIGNAL_LED_HIGH]);
			}
		} else {
			sysmgr_err("sa dev[%s] base on dev[%s] have NO opt method\n", sa_dev->name, gpio->name);
		}
		break;

	case eLED_OPT_SET_5G_SIGNAL_NONE:
		PREPARE_LED_OFF_CFG(&(priv->led_5g[eSIGNAL_LED_HIGH].led));
		PREPARE_LED_OFF_CFG(&(priv->led_5g[eSIGNAL_LED_NORMAL].led));
		PREPARE_LED_OFF_CFG(&(priv->led_5g[eSIGNAL_LED_LOW].led));
		if (gpio->opt != NULL) {
			/* turn off led, ignore power save */
			gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_5g[eSIGNAL_LED_LOW]);
			gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_5g[eSIGNAL_LED_NORMAL]);
			gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_5g[eSIGNAL_LED_HIGH]);
		} else {
			sysmgr_err("sa dev[%s] base on dev[%s] have NO opt method\n", sa_dev->name, gpio->name);
		}
		break;

	case eLED_OPT_SET_5G_SCAN:
		PREPARE_SIGNAL_LED_MARQUEE(priv->led_5g, 4000);
		if (gpio->opt != NULL) {
			if (priv->power_save != eBOOL_TRUE) {
				gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_5g[eSIGNAL_LED_LOW]);
				gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_5g[eSIGNAL_LED_NORMAL]);
				gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_5g[eSIGNAL_LED_HIGH]);
			}
		} else {
			sysmgr_err("sa dev[%s] base on dev[%s] have NO opt method\n", sa_dev->name, gpio->name);
		}
		break;

	case eLED_OPT_SET_5G_WPS_PROCESS:
		PREPARE_SIGNAL_LED_MARQUEE_REVERSE(priv->led_5g, 4000);
		if (gpio->opt != NULL) {
			if (priv->power_save != eBOOL_TRUE) {
				gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_5g[eSIGNAL_LED_LOW]);
				gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_5g[eSIGNAL_LED_NORMAL]);
				gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_5g[eSIGNAL_LED_HIGH]);
			}
		} else {
			sysmgr_err("sa dev[%s] base on dev[%s] have NO opt method\n", sa_dev->name, gpio->name);
		}
		break;

	case eLED_OPT_SET_5G_WPS_SUCCESS:
		PREPARE_SIGNAL_LED_BLINK(priv->led_5g, 5);
		if (gpio->opt != NULL) {
			if (priv->power_save != eBOOL_TRUE) {
				gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_5g[eSIGNAL_LED_LOW]);
				gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_5g[eSIGNAL_LED_NORMAL]);
				gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_5g[eSIGNAL_LED_HIGH]);
			}
		} else {
			sysmgr_err("sa dev[%s] base on dev[%s] have NO opt method\n", sa_dev->name, gpio->name);
		}
		break;

	case eLED_OPT_SET_5G_WPS_FAIL:
	case eLED_OPT_SET_5G_WPS_OVERLAP:
		PREPARE_SIGNAL_LED_BLINK_FAST(priv->led_5g, 10);
		if (gpio->opt != NULL) {
			if (priv->power_save != eBOOL_TRUE) {
				gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_5g[eSIGNAL_LED_LOW]);
				gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_5g[eSIGNAL_LED_NORMAL]);
				gpio->opt(gpio, eGPIO_OPT_SET_LED, &priv->led_5g[eSIGNAL_LED_HIGH]);
			}
		} else {
			sysmgr_err("sa dev[%s] base on dev[%s] have NO opt method\n", sa_dev->name, gpio->name);
		}
		break;

	case eLED_OPT_CLEAN:
		ret = clean_rt_led_dev(sa_dev);
		break;

	default:
		sysmgr_err("Unknown operation:%d\n", opt);
		ret = eRET_INVALID_ARG;
	}

	return (ret);
}

e_ret init_rt_button_dev(tsa_dev * sa_dev)
{

	tsa_dev *gpio;
	trt_button_priv *priv;

	if (sa_dev->init == eBOOL_TRUE) {
		sysmgr_dbg("sa dev[%s] already init.\n", sa_dev->name);
		return (eRET_SUCCESS);
	}

	priv = malloc(sizeof(*priv));
	if (priv == NULL) {
		sysmgr_err("malloc(%d) failed, err: %d,[%s]\n", sizeof(*priv), errno, strerror(errno));
		return (eRET_NO_RESOURCE);
	}

	memset(priv, 0, sizeof(*priv));

	priv->button[eBUTTON_WPS].gpio = eGPIO_NO_12;
	priv->button[eBUTTON_RESET].gpio = eGPIO_NO_13;

	priv->button[eBUTTON_WPS].dir = eGPIO_DIR_IN;
	priv->button[eBUTTON_RESET].dir = eGPIO_DIR_IN;

	/* Set Button to INPUT mode. */
	gpio = sa_dev->based_on_dev;
	if (gpio != NULL) {
		if (gpio->opt != NULL) {
			gpio->opt(gpio, eGPIO_OPT_SET_DIR, &priv->button[eBUTTON_WPS]);
			gpio->opt(gpio, eGPIO_OPT_SET_DIR, &priv->button[eBUTTON_RESET]);
		} else {
			sysmgr_err("sa dev[%s] base on dev[%s] have NO opt method\n", sa_dev->name, gpio->name);
		}
	} else {
		sysmgr_err("sa dev[%s] lose base on dev\n", sa_dev->name);
	}

	gpio->opt(gpio, eGPIO_OPT_ENABLE_IRQ, NULL);

	priv->button[eBUTTON_WPS].pid = getpid();
	priv->button[eBUTTON_RESET].pid = getpid();
	if (gpio != NULL) {
		if (gpio->opt != NULL) {
			gpio->opt(gpio, eGPIO_OPT_REG_PROCCESS, &priv->button[eBUTTON_WPS]);
			gpio->opt(gpio, eGPIO_OPT_REG_PROCCESS, &priv->button[eBUTTON_RESET]);
		} else {
			sysmgr_err("sa dev[%s] base on dev[%s] have NO opt method\n", sa_dev->name, gpio->name);
		}
	} else {
		sysmgr_err("sa dev[%s] lose base on dev\n", sa_dev->name);
	}

	sa_dev->priv = priv;
	sa_dev->init = eBOOL_TRUE;
	return (eRET_SUCCESS);
}

e_ret clean_rt_button_dev(tsa_dev * sa_dev)
{
	if (sa_dev->init == eBOOL_FALSE) {
		sysmgr_dbg("sa dev[%s] already clean.\n", sa_dev->name);
		return (eRET_SUCCESS);
	}

	sa_dev->init = eBOOL_FALSE;
	return (eRET_SUCCESS);
}

e_ret rt_button_dev_opt(tsa_dev * sa_dev, unsigned char opt, void *cfg)
{
	e_ret ret;
	tsa_dev *gpio;
	tgpio_opt_cfg gpio_opt;
	trt_button_priv *priv = sa_dev->priv;

	ret = eRET_SUCCESS;
	switch (opt) {
	case eBUTTON_OPT_INIT:
		ret = init_rt_button_dev(sa_dev);
		break;

	case eBUTTON_OPT_CLEAN:
		ret = clean_rt_button_dev(sa_dev);
		break;

	case eBUUTON_OPT_CHECK:
		/* Read GPIO data. */
		if (priv != NULL) {
			gpio = sa_dev->based_on_dev;
			if (gpio != NULL) {
				if (gpio->opt != NULL) {
					gpio->opt(gpio, eGPIO_OPT_DISABLE_IRQ, NULL);

					gpio_opt.gpio = priv->button[eBUTTON_RESET].gpio;
					gpio->opt(gpio, eGPIO_OPT_GET_INTP, &gpio_opt);
					switch (gpio_opt.intp) {
					case eBUTTON_INPT_LONG_PRESS:
						/* do factory default */
						sysmgr_dbg("RESET Button Long Press.\n");
						pf_dep_reset_cfg();
						pf_dep_reboot();
						break;

					case eBUTTON_INPT_SHORT_PRESS:
						/* do reboot */
						sysmgr_dbg("RESET Button Short Press.\n");
						pf_dep_reboot();
						break;
					case eBUTTON_INPT_NONE:
					default:
						/* do NOTHING */
						break;
					}

					gpio_opt.gpio = priv->button[eBUTTON_WPS].gpio;
					gpio->opt(gpio, eGPIO_OPT_GET_INTP, &gpio_opt);
					switch (gpio_opt.intp) {
					case eBUTTON_INPT_LONG_PRESS:
						/* Delivery WPS event to its manager. */
						sysmgr_dbg("WPS Button Long Pressd\n");
						dp_wlan_wps_proc(eBUTTON_INPT_LONG_PRESS);
						break;

					case eBUTTON_INPT_SHORT_PRESS:
						/* Delivery WPS event to its manager. */
						sysmgr_dbg("WPS Button Short Pressd\n");
						dp_wlan_wps_proc(eBUTTON_INPT_SHORT_PRESS);
						break;

					case eBUTTON_INPT_NONE:
					default:
						/* do NOTHING */
						break;
					}

					gpio->opt(gpio, eGPIO_OPT_ENABLE_IRQ, NULL);
				} else {
					sysmgr_err("sa dev[%s] base on dev[%s] have NO opt method\n", sa_dev->name,
						   gpio->name);
				}
			} else {
				sysmgr_err("sa dev[%s] lose base on dev\n", sa_dev->name);
			}
		} else {
			sysmgr_err("sa dev[%s] get NULL priv.\n", sa_dev->name);
			ret = eRET_INVALID_STATE;
		}
		break;
	}

	return (ret);
}

e_ret init_rt_mode_sw_dev(tsa_dev * sa_dev)
{

	tsa_dev *gpio;
	trt_mode_sw_priv *priv;
	extern unsigned char dev_mode;

	if (sa_dev->init == eBOOL_TRUE) {
		sysmgr_dbg("sa dev[%s] already init.\n", sa_dev->name);
		return (eRET_SUCCESS);
	}

	priv = malloc(sizeof(*priv));
	if (priv == NULL) {
		sysmgr_err("malloc(%d) failed, err: %d,[%s]\n", sizeof(*priv), errno, strerror(errno));
		return (eRET_NO_RESOURCE);
	}

	memset(priv, 0, sizeof(*priv));

	priv->mode_sw.gpio = eGPIO_NO_69;
	priv->mode_sw.dir = eGPIO_DIR_IN;

	/* Set Button to INPUT mode. */
	gpio = sa_dev->based_on_dev;
	if (gpio != NULL) {
		if (gpio->opt != NULL) {
			gpio->opt(gpio, eGPIO_OPT_SET_DIR, &priv->mode_sw);
			gpio->opt(gpio, eGPIO_OPT_READ, &priv->mode_sw);

			if (priv->mode_sw.rw_value == 0) {
				dev_mode = eDP_MODE_BRIDGE_AP;
				sysmgr_info("System Mode: [%s]\n", "Ap Mode");
			} else {
				dev_mode = eDP_MODE_BRIDGE_REPEATER;
				sysmgr_info("System Mode: [%s]\n", "Repeater Mode");
			}
		} else {
			sysmgr_err("sa dev[%s] base on dev[%s] have NO opt method\n", sa_dev->name, gpio->name);
		}
	} else {
		sysmgr_err("sa dev[%s] lose base on dev\n", sa_dev->name);
	}

	sa_dev->priv = priv;
	sa_dev->init = eBOOL_TRUE;
	return (eRET_SUCCESS);
}

e_ret clean_rt_mode_sw_dev(tsa_dev * sa_dev)
{
	if (sa_dev->init == eBOOL_FALSE) {
		sysmgr_dbg("sa dev[%s] already clean.\n", sa_dev->name);
		return (eRET_SUCCESS);
	}

	sa_dev->init = eBOOL_FALSE;
	return (eRET_SUCCESS);
}

e_ret rt_mode_sw_dev_opt(tsa_dev * sa_dev, unsigned char opt, void *cfg)
{
	e_ret ret;
	unsigned char mode;
	tsa_dev *gpio;
	tgpio_opt_cfg gpio_opt;
	trt_mode_sw_priv *priv = sa_dev->priv;
	extern unsigned char dev_mode;

	ret = eRET_SUCCESS;
	switch (opt) {
	case eMODE_SW_OPT_INIT:
		ret = init_rt_mode_sw_dev(sa_dev);
		break;

	case eMODE_SW_OPT_CLEAN:
		ret = clean_rt_mode_sw_dev(sa_dev);
		break;

	case eMODE_SW_OPT_CHECK:
		/* Set Button to INPUT mode. */
		gpio = sa_dev->based_on_dev;
		if (gpio != NULL) {
			if (gpio->opt != NULL) {
				priv->mode_sw.dir = eGPIO_DIR_IN;
				gpio->opt(gpio, eGPIO_OPT_SET_DIR, &priv->mode_sw);

				gpio_opt.gpio = priv->mode_sw.gpio;
				gpio->opt(gpio, eGPIO_OPT_READ, &gpio_opt);
				if (gpio_opt.rw_value == 0) {
					mode = eDP_MODE_BRIDGE_AP;
				} else {
					mode = eDP_MODE_BRIDGE_REPEATER;
				}
				if (mode != dev_mode) {
					if (dev_mode == eDP_MODE_BRIDGE_AP) {
						sysmgr_err("System Mode Change [Ap Mode] -> [Repeater Mode]\n");
					} else {
						sysmgr_err("System Mode Change [Repeater Mode] -> [Ap Mode]\n");
					}
					pf_dep_reboot();
					//os_dep_killall("sysmgr", "-SIGHUP");
				}
			} else {
				sysmgr_err("sa dev[%s] base on dev[%s] have NO opt method\n", sa_dev->name, gpio->name);
			}
		} else {
			sysmgr_err("sa dev[%s] lose base on dev\n", sa_dev->name);
		}
		break;
	}

	return (ret);
}

e_ret pf_dep_dp_devs_init(tdata_paths * dp)
{
	e_ret ret = eRET_SUCCESS;

	ret |= dp_dev_prepare(&raeth, init_raeth_dev, clean_raeth_dev);
	ret |= dp_dev_prepare(&rt2860v2_ap, init_rt2860v2_ap_dev, clean_rt2860v2_ap_dev);
	ret |= dp_dev_prepare(&rlt_wifi, init_rlt_wifi_dev, clean_rlt_wifi_dev);

	return (ret);
}

e_ret pf_dep_sa_devs_init(tsys_admin * sa)
{
	e_ret ret = eRET_SUCCESS;

	INIT_LIST_HEAD(&sa->all_devs);

	INIT_LIST_HEAD(&rt_gpio.list);
	INIT_LIST_HEAD(&rt_led.list);
	INIT_LIST_HEAD(&rt_button.list);
	INIT_LIST_HEAD(&rt_mode_sw.list);

	list_add(&rt_gpio.list, &sa->all_devs);
	list_add(&rt_led.list, &sa->all_devs);
	list_add(&rt_button.list, &sa->all_devs);
	list_add(&rt_mode_sw.list, &sa->all_devs);

	ret |= sa_dev_prepare(&rt_gpio, rt_gpio_dev_opt);
	ret |= sa_dev_prepare(&rt_led, rt_led_dev_opt);
	ret |= sa_dev_prepare(&rt_button, rt_button_dev_opt);
	ret |= sa_dev_prepare(&rt_mode_sw, rt_mode_sw_dev_opt);

	return (ret);
}

e_ret pf_dep_sa_devs_setup(tsys_admin * sa)
{
	return (eRET_SUCCESS);
}

int raesw_init(void)
{
	int esw_fd = socket(AF_INET, SOCK_DGRAM, 0);
	if (esw_fd < 0) {
		sysmgr_err("socket() failed, err: %d,[%s]\n", errno, strerror(errno));
	}
	return (esw_fd);
}

void raesw_finish(int fd)
{
	if (fd > 0) {
		close(fd);
	}
}

void raesw_table_clear(int fd)
{
	int value;
	raesw_reg_write(fd, REG_ESW_WT_MAC_ATC, 0x8002);
	usleep(5000);
	raesw_reg_read(fd, REG_ESW_WT_MAC_ATC, &value);
}

#if defined (CONFIG_RALINK_MT7621)

int raesw_reg_read(int fd, int offset, int *value)
{
	int rc;
	esw_reg reg;
	struct ifreq ifr;

	ra_mii_ioctl_data mii;
	strncpy(ifr.ifr_name, "eth2", 5);
	ifr.ifr_data = &mii;

	mii.phy_id = 0x1f;
	mii.reg_num = offset;

	rc = ioctl(fd, RAETH_MII_READ, &ifr);
	if (rc < 0) {
		sysmgr_err("ioctl() failed, err: %d,[%s]\n", errno, strerror(errno));
		return (rc);
	}
	*value = mii.val_out;
	return 0;

}

int raesw_reg_write(int fd, int offset, int value)
{
	int rc;
	esw_reg reg;
	struct ifreq ifr;
	ra_mii_ioctl_data mii;

	strncpy(ifr.ifr_name, "eth2", 5);
	ifr.ifr_data = &mii;

	mii.phy_id = 0x1f;
	mii.reg_num = offset;
	mii.val_in = value;

	rc = ioctl(fd, RAETH_MII_WRITE, &ifr);
	if (rc < 0) {
		sysmgr_err("ioctl() failed, err: %d,[%s]\n", errno, strerror(errno));
		return (rc);
	}
	return 0;
}

int raesw_vlan_set(int fd, int idx, int vid, unsigned int portmap, int stag, e_bool eg_con_enable,
		   e_bool eg_tag_control)
{
	unsigned int i, value, value2;
	unsigned char eg_con = 0;
	unsigned char eg_tag = 0;

	//set vlan member
	value = (portmap << 16);
	//value |= (idx << 1);//fid
	value |= (1 << 30);	//IVL=1
	value |= ((stag & 0xfff) << 4);	//stag
	value |= 1;		//valid

	if (eg_con_enable) {
		value |= (eg_con << 29);	//eg_con
		value |= (1 << 28);	//eg tag control enable
	}

	if (eg_tag_control) {
		value |= (1 << 28);	//eg tag control enable
		value2 = eg_tag;	//port 0
		value2 |= eg_tag << 2;	//port  1
		value2 |= eg_tag << 4;	//port 2
		raesw_reg_write(fd, REG_ESW_VLAN_VAWD2, value2);
	}
	raesw_reg_write(fd, REG_ESW_VLAN_VAWD1, value);

	//value = (0x80001000 + idx);  //w_vid_cmd
	value = (0x80001000 + vid);	//w_vid_cmd
	raesw_reg_write(fd, REG_ESW_VLAN_VTCR, value);

	for (i = 0; i < 300; i++) {
		usleep(1000);
		raesw_reg_read(fd, REG_ESW_VLAN_VTCR, &value);
		if ((value & 0x80000000) == 0) {	//table busy
			break;
		}
	}

	if (i == 300) {
		sysmgr_err("Config vlan timeout.\n");
		return (-1);
	}
	return (0);

}

#else
int raesw_reg_read(int fd, int offset, int *value)
{
	int rc;
	struct ifreq ifr;
	esw_reg reg;

	reg.off = offset;
	strncpy(ifr.ifr_name, "eth2", 5);
	ifr.ifr_data = &reg;

	rc = ioctl(fd, RAETH_ESW_REG_READ, &ifr);
	if (rc < 0) {
		sysmgr_err("ioctl() failed, err: %d,[%s]\n", errno, strerror(errno));
		return (rc);
	}
	*value = reg.val;
	return 0;
}

int raesw_reg_write(int fd, int offset, int value)
{
	int rc;
	esw_reg reg;
	struct ifreq ifr;

	reg.off = offset;
	reg.val = value;
	strncpy(ifr.ifr_name, "eth2", 5);
	ifr.ifr_data = &reg;

	rc = ioctl(fd, RAETH_ESW_REG_WRITE, &ifr);
	if (rc < 0) {
		sysmgr_err("ioctl() failed, err: %d,[%s]\n", errno, strerror(errno));
		return (rc);
	}
	return 0;
}

int raesw_vlan_set(int fd, int idx, int vid, unsigned int portmap, int stag, e_bool eg_con_enable,
		   e_bool eg_tag_control)
{
	unsigned int i, value, value2;
	unsigned char eg_con = 0;
	unsigned char eg_tag = 0;

	stag = 0;
	//set vlan identifier
	raesw_reg_read(fd, REG_ESW_VLAN_ID_BASE + 4 * (idx / 2), &value);
	if (idx % 2 == 0) {
		value &= 0xfff000;
		value |= vid;
	} else {
		value &= 0xfff;
		value |= (vid << 12);
	}
	raesw_reg_write(fd, REG_ESW_VLAN_ID_BASE + 4 * (idx / 2), value);

	//set vlan member
	value = (portmap << 16);
	value |= (1 << 30);	//IVL=1
	value |= ((stag & 0xfff) << 4);	//stag

	value |= 1;		//valid

	if (eg_con_enable) {
		value |= (eg_con << 29);	//eg_con
		value |= (1 << 28);	//eg tag control enable
	}

	if (eg_tag_control) {
		value |= (1 << 28);	//eg tag control enable
		value2 = eg_tag;	//port 0
		value2 |= eg_tag << 2;	//port  1
		value2 |= eg_tag << 4;	//port 2
		raesw_reg_write(fd, REG_ESW_VLAN_VAWD2, value2);
	}

	raesw_reg_write(fd, REG_ESW_VLAN_VAWD1, value);

	value = (0x80001000 + idx);	//w_vid_cmd
	raesw_reg_write(fd, REG_ESW_VLAN_VTCR, value);

	for (i = 0; i < 300; i++) {
		usleep(1000);
		raesw_reg_read(fd, REG_ESW_VLAN_VTCR, &value);
		if ((value & 0x80000000) == 0) {	//table busy
			break;
		}
	}

	if (i == 300) {
		sysmgr_err("Config vlan timeout.\n");
		return (-1);
	}
	return (0);
}

#endif
